<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    Linux服务器高并发调优实战.md |  
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-linux/Linux服务器高并发调优实战"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  Linux服务器高并发调优实战.md
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/linux/Linux%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%AB%98%E5%B9%B6%E5%8F%91%E8%B0%83%E4%BC%98%E5%AE%9E%E6%88%98/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/linux/">linux</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">3.8k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">14 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="Linux服务器高并发调优实战"><a href="#Linux服务器高并发调优实战" class="headerlink" title="Linux服务器高并发调优实战"></a>Linux服务器高并发调优实战</h2><p><strong>高并发情况下Linux系统及kernel参数优化</strong></p>
<p>众所周知在默认参数情况下Linux对高并发支持并不好，主要受限于单进程最大打开文件数限制、内核TCP参数方面和IO事件分配机制等。下面就从几方面来调整使Linux系统能够支持高并发环境。</p>
<h3 id="iptables相关"><a href="#iptables相关" class="headerlink" title="iptables相关"></a>iptables相关</h3><p>如非必须，关掉或卸载iptables防火墙，并阻止kernel加载iptables模块。这些模块会影响并发性能。</p>
<h4 id="单进程最大打开文件数限制"><a href="#单进程最大打开文件数限制" class="headerlink" title="单进程最大打开文件数限制"></a>单进程最大打开文件数限制</h4><p>一般的发行版，限制单进程最大可以打开1024个文件，这是远远不能满足高并发需求的，调整过程如下：</p>
<p>在#号提示符下敲入：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># ulimit –n 65535</span><br></pre></td></tr></table></figure>

<p>将root启动的单一进程的最大可以打开的文件数设置为65535个。如果系统回显类似于“Operationnotpermitted”之类的话，说明上述限制修改失败，实际上是因为在中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。因此，就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。</p>
<p>第一步，修改limits.conf文件，并添加：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;etc&#x2F;security&#x2F;limits.conf</span><br><span class="line">* soft nofile 65535</span><br><span class="line">* hard nofile 65535</span><br></pre></td></tr></table></figure>

<p>其中’*’号表示修改所有用户的限制；soft或hard指定要修改软限制还是硬限制；65536则指定了想要修改的新的限制值，即最大打开文件数(请注意软限制值要小于或等于硬限制)。修改完后保存文件。</p>
<p>第二步，修改/etc/pam.d/login文件，在文件中添加如下行：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;etc&#x2F;pam.d&#x2F;login</span><br><span class="line">sessionrequired &#x2F;lib&#x2F;security&#x2F;pam_limits.so</span><br></pre></td></tr></table></figure>

<p>这是告诉Linux在用户完成系统登录后，应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制)，而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。修改完后保存此文件。</p>
<p>第三步，查看Linux系统级的最大打开文件数限制，使用如下命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># cat&#x2F;proc&#x2F;sys&#x2F;fs&#x2F;file-max</span><br><span class="line">32568</span><br></pre></td></tr></table></figure>

<p>这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)32568个文件，是Linux系统级硬限制，所有用户级的打开文件数限制都不应超过这个数值。通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制，如果没有特殊需要，不应该修改此限制，除非想为用户级打开文件数限制设置超过此限制的值。修改此硬限制的方法是修改/etc/sysctl.conf文件内fs.file-max= 131072</p>
<p>这是让Linux在启动完成后强行将系统级打开文件数硬限制设置为131072。修改完后保存此文件。</p>
<p>完成上述步骤后重启系统，一般情况下就可以将Linux系统对指定用户的单一进程允许同时打开的最大文件数限制设为指定的数值。如果重启后用ulimit-n命令查看用户可打开文件数限制仍然低于上述步骤中设置的最大值，这可能是因为在用户登录脚本/etc/profile中使用ulimit-n命令已经将用户可同时打开的文件数做了限制。由于通过ulimit-n修改系统对用户可同时打开文件的最大数限制时，新修改的值只能小于或等于上次ulimit-n设置的值，因此想用此命令增大这个限制值是不可能的。所以，如果有上述问题存在，就只能去打开/etc/profile脚本文件，在文件中查找是否使用了ulimit-n限制了用户可同时打开的最大文件数量，如果找到，则删除这行命令，或者将其设置的值改为合适的值，然后保存文件，用户退出并重新登录系统即可。</p>
<p>通过上述步骤，就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。</p>
<h4 id="内核TCP参数方面"><a href="#内核TCP参数方面" class="headerlink" title="内核TCP参数方面"></a>内核TCP参数方面</h4><p>Linux系统下，TCP连接断开后，会以TIME_WAIT状态保留一定的时间，然后才会释放端口。当并发请求过多的时候，就会产生大量的TIME_WAIT状态的连接，无法及时断开的话，会占用大量的端口资源和服务器资源。这个时候我们可以优化TCP的内核参数，来及时将TIME_WAIT状态的端口清理掉。</p>
<p>下面介绍的方法只对拥有大量TIME_WAIT状态的连接导致系统资源消耗有效，如果不是这种情况下，效果可能不明显。可以使用netstat命令去查TIME_WAIT状态的连接状态，输入下面的组合命令，查看当前TCP连接的状态和对应的连接数量：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># netstat-n | awk ‘&#x2F;^tcp&#x2F; &#123;++S[$NF]&#125; END &#123;for(a in S) print a, S[a]&#125;’</span><br><span class="line">这个命令会输出类似下面的结果：</span><br></pre></td></tr></table></figure>



<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LAST_ACK16SYN_RECV348ESTABLISHED70FIN_WAIT1229FIN_WAIT230CLOSING33TIME_WAIT18098</span><br></pre></td></tr></table></figure>

<p>我们只用关心TIME_WAIT的个数，在这里可以看到，有18000多个TIME_WAIT，这样就占用了18000多个端口。要知道端口的数量只有65535个，占用一个少一个，会严重的影响到后继的新连接。这种情况下，我们就有必要调整下Linux的TCP内核参数，让系统更快的释放TIME_WAIT连接。</p>
<p>编辑配置文件:/etc/sysctl.conf，在这个文件中，加入下面的几行内容：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;etc&#x2F;sysctl.confnet.ipv4.tcp_syncookies&#x3D; 1net.ipv4.tcp_tw_reuse&#x3D; 1net.ipv4.tcp_tw_recycle&#x3D; 1net.ipv4.tcp_fin_timeout&#x3D; 30</span><br></pre></td></tr></table></figure>

<p>输入下面的命令，让内核参数生效：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># sysctl-p</span><br></pre></td></tr></table></figure>

<p>简单的说明上面的参数的含义：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_syncookies&#x3D; 1</span><br></pre></td></tr></table></figure>

<p>表示开启SYNCookies。当出现SYN等待队列溢出时，启用cookies来处理，可防范少量SYN攻击，默认为0，表示关闭；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_tw_reuse&#x3D; 1</span><br></pre></td></tr></table></figure>

<p>表示开启重用。允许将TIME-WAITsockets重新用于新的TCP连接，默认为0，表示关闭；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_tw_recycle&#x3D; 1</span><br></pre></td></tr></table></figure>

<p>表示开启TCP连接中TIME-WAITsockets的快速回收，默认为0，表示关闭；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_fin_timeout</span><br></pre></td></tr></table></figure>

<p>修改系統默认的TIMEOUT 时间。</p>
<p>在经过这样的调整之后，除了会进一步提升服务器的负载能力之外，还能够防御小流量程度的DoS、CC和SYN攻击。</p>
<p>此外，如果你的连接数本身就很多，我们可以再优化一下TCP的可使用端口范围，进一步提升服务器的并发能力。依然是往上面的参数文件中，加入下面这些配置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_keepalive_time&#x3D; 1200net.ipv4.ip_local_port_range&#x3D; 1024 65535net.ipv4.tcp_max_syn_backlog&#x3D; 8192net.ipv4.tcp_max_tw_buckets&#x3D; 5000</span><br></pre></td></tr></table></figure>

<p>这几个参数，建议只在流量非常大的服务器上开启，会有显著的效果。一般的流量小的服务器上，没有必要去设置这几个参数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_keepalive_time&#x3D; 1200</span><br></pre></td></tr></table></figure>

<p>表示当keepalive起用的时候，TCP发送keepalive消息的频度。缺省是2小时，改为20分钟。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip_local_port_range&#x3D; 1024 65535</span><br></pre></td></tr></table></figure>

<p>表示用于向外连接的端口范围。缺省情况下很小，改为1024到65535。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_max_syn_backlog&#x3D; 8192</span><br></pre></td></tr></table></figure>

<p>表示SYN队列的长度，默认为1024，加大队列长度为8192，可以容纳更多等待连接的网络连接数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_max_tw_buckets&#x3D; 5000</span><br></pre></td></tr></table></figure>

<p>表示系统同时保持TIME_WAIT的最大数量，如果超过这个数字，TIME_WAIT将立刻被清除并打印警告信息。默认为180000，改为5000。此项参数可以控制TIME_WAIT的最大数量，只要超出了。</p>
<p>内核其他TCP参数说明</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_max_syn_backlog&#x3D; 65535</span><br></pre></td></tr></table></figure>

<p>记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言，缺省值是1024，小内存的系统则是128。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.core.netdev_max_backlog&#x3D; 32768</span><br></pre></td></tr></table></figure>

<p>每个网络接口接收数据包的速率比内核处理这些包的速率快时，允许送到队列的数据包的最大数目。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.core.somaxconn&#x3D; 32768</span><br></pre></td></tr></table></figure>

<p>例如web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128，而nginx定义的NGX_LISTEN_BACKLOG默认为511，所以有必要调整这个值。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.core.wmem_default&#x3D; 8388608net.core.rmem_default&#x3D; 8388608net.core.rmem_max&#x3D; 16777216 #最大socket读buffer,可参考的优化值:873200net.core.wmem_max&#x3D; 16777216 #最大socket写buffer,可参考的优化值:873200net.ipv4.tcp_timestsmps&#x3D; 0</span><br></pre></td></tr></table></figure>

<p>时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_synack_retries&#x3D; 2</span><br></pre></td></tr></table></figure>

<p>为了打开对端的连接，内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_syn_retries&#x3D; 2</span><br></pre></td></tr></table></figure>

<p>在内核放弃建立连接之前发送SYN包的数量。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#net.ipv4.tcp_tw_len&#x3D; 1net.ipv4.tcp_tw_reuse&#x3D; 1</span><br></pre></td></tr></table></figure>

<p>开启重用。允许将TIME-WAITsockets重新用于新的TCP连接。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_wmem&#x3D; 8192 436600 873200TCP写buffer,可参考的优化值:8192 436600 873200net.ipv4.tcp_rmem &#x3D; 32768 436600 873200TCP读buffer,可参考的优化值:32768 436600 873200net.ipv4.tcp_mem&#x3D; 94500000 91500000 92700000同样有3个值,意思是:net.ipv4.tcp_mem[0]:低于此值，TCP没有内存压力。net.ipv4.tcp_mem[1]:在此值下，进入内存压力阶段。net.ipv4.tcp_mem[2]:高于此值，TCP拒绝分配socket。上述内存单位是页，而不是字节。可参考的优化值是:7864321048576 1572864net.ipv4.tcp_max_orphans&#x3D; 3276800</span><br></pre></td></tr></table></figure>

<p>系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。</p>
<p>如果超过这个数字，连接将即刻被复位并打印出警告信息。</p>
<p>这个限制仅仅是为了防止简单的DoS攻击，不能过分依靠它或者人为地减小这个值，</p>
<p>更应该增加这个值(如果增加了内存之后)。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_fin_timeout&#x3D; 30</span><br></pre></td></tr></table></figure>

<p>如果套接字由本端要求关闭，这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接，甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒，你可以按这个设置，但要记住的是，即使你的机器是一个轻载的WEB服务器，也有因为大量的死套接字而内存溢出的风险，FIN-WAIT-2的危险性比FIN-WAIT-1要小，因为它最多只能吃掉1.5K内存，但是它们的生存期长些。</p>
<p>同时还涉及到一个TCP 拥塞算法的问题，你可以用下面的命令查看本机提供的拥塞算法控制模块：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sysctlnet.ipv4.tcp_available_congestion_control</span><br><span class="line">对于几种算法的分析，详情可以参考下：TCP拥塞控制算法的优缺点、适用环境、性能分析，比如高延时可以试用hybla，中等延时可以试用htcp算法等。</span><br></pre></td></tr></table></figure>

<p>如果想设置TCP 拥塞算法为hybla</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_congestion_control&#x3D;hybla</span><br><span class="line">额外的，对于内核版高于于3.7.1的，我们可以开启tcp_fastopen：</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_fastopen&#x3D; 3</span><br></pre></td></tr></table></figure>

<h4 id="IO事件分配机制"><a href="#IO事件分配机制" class="headerlink" title="IO事件分配机制"></a>IO事件分配机制</h4><p>在Linux启用高并发TCP连接，必须确认应用程序是否使用了合适的网络I/O技术和I/O事件分派机制。可用的I/O技术有同步I/O，非阻塞式同步I/O，以及异步I/O。在高TCP并发的情形下，如果使用同步I/O，这会严重阻塞程序的运转，除非为每个TCP连接的I/O创建一个线程。但是，过多的线程又会因系统对线程的调度造成巨大开销。因此，在高TCP并发的情形下使用同步I/O是不可取的，这时可以考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select()，poll()，epoll等机制。异步I/O的技术就是使用AIO。</p>
<p>从I/O事件分派机制来看，使用select()是不合适的，因为它所支持的并发连接数有限(通常在1024个以内)。如果考虑性能，poll()也是不合适的，尽管它可以支持的较高的TCP并发数，但是由于其采用“轮询”机制，当并发数较高时，其运行效率相当低，并可能存在I/O事件分派不均，导致部分TCP连接上的I/O出现“饥饿”现象。而如果使用epoll或AIO，则没有上述问题(早期Linux内核的AIO技术实现是通过在内核中为每个I/O请求创建一个线程来实现的，这种实现机制在高并发TCP连接的情形下使用其实也有严重的性能问题。但在最新的Linux内核中，AIO的实现已经得到改进)。</p>
<p>综上所述，在开发支持高并发TCP连接的Linux应用程序时，应尽量使用epoll或AIO技术来实现并发的TCP连接上的I/O控制，这将为提升程序对高并发TCP连接的支持提供有效的I/O保证。</p>
<p>经过这样的优化配置之后，服务器的TCP并发处理能力会显著提高。以上配置仅供参考，用于生产环境请根据自己的实际情况调整观察再调整。</p>
<p>扫码关注</p>
<p>绝对干货的公众号</p>
<p><img src="http://iubest.gitee.io/pic/640-1601001376240.gif" alt="img"></p>
<p><img src="http://iubest.gitee.io/pic/640-1601001369023.webp" alt="img"></p>
<p><img src="http://iubest.gitee.io/pic/640-1601001368966.gif" alt="img"></p>
<p>推荐阅读</p>
<p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484733&idx=1&sn=8a5fcad29d0fea680f8ba93fea44c0da&chksm=fe7b0a01c90c8317e174ca316b1c15dd11bdfe41f1c70b3331576683ca87a0d2e8de267b99ad&token=1047100134&lang=zh_CN&scene=21#wechat_redirect">
</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484673&idx=1&sn=f1c6fd7c00049734ed97121b5c401c6a&chksm=fe7b0a3dc90c832b226d1df991f413a4dfaebce8c4be4549ebd777a688e08f61f651b1bcd337&scene=21#wechat_redirect">实战项目–kkitDeploy（客户端版）(强推,已开源)</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485054&idx=1&sn=76b2afbaa98227a0efb06e1c5e53be17&chksm=fe7b0942c90c8054172c8ca3020ddeb28d32cee3d3807efc6e7c9b063d128ee31dcdc831f457&scene=21#wechat_redirect">实战项目–kkitDeploy（PaaS版）(强推)</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484871&idx=1&sn=eea93b8ec270325f961e41038eade6d4&chksm=fe7b0afbc90c83ed0251453b69c1ac0e48db7444dd27caf8d8830b03235190cb7802058c64b6&scene=21#wechat_redirect">实战项目–kkit3.0功能介绍(强推)</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247483880&idx=1&sn=5a38522f8639330a62fd8bd1fe032972&chksm=fe7b0ed4c90c87c26e4444cf21254dc613258082b28b4a6707a041edadb982ab0d13b994749a&scene=21#wechat_redirect">实战项目–kkit1.0功能介绍(强推,已开源)</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484768&idx=1&sn=4016a88bc54e9fe67b7b6d76fa3cedb4&chksm=fe7b0a5cc90c834ae4e95f951324ffe2a9991b502da194c507c6d02b7b277403f00fcc123940&scene=21#wechat_redirect">面试题之—网络安全（2019年10月更新）</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484733&idx=1&sn=8a5fcad29d0fea680f8ba93fea44c0da&chksm=fe7b0a01c90c8317e174ca316b1c15dd11bdfe41f1c70b3331576683ca87a0d2e8de267b99ad&scene=21#wechat_redirect">面试题之—k8s（2019年10月更新）</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484360&idx=1&sn=b0f6bdca5a3f4f4ac3c53c9f1ef9cbc9&chksm=fe7b0cf4c90c85e2ee1afb0571603426ab172c5c2e1d85d4763b086a562f5673973d4230e17a&scene=21#wechat_redirect">面试题之—运维工程师经典面试汇总（一）</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484747&idx=1&sn=b49dfe531c0c1331200a7e4947f65976&chksm=fe7b0a77c90c8361f2ac4f3784681c7580c3699bf0f49d930b56999a2bd8e3e62fa3e6c9b521&scene=21#wechat_redirect">经历贴之—外包接活却坐牢456天完整记录（强推）</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484455&idx=1&sn=da41f73724909d3051343133cd53db74&chksm=fe7b0b1bc90c820d954f649d76be230f651d62431858eb06bae8f5deaa36e66339f515116373&scene=21#wechat_redirect">脚本之—短信轰炸机</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484740&idx=1&sn=0683fbf63643218045c1b8cd522137ec&chksm=fe7b0a78c90c836e85c254cce4b8c6897579fd4f6a1386b59084b6fe4cfdd3a72bbcbfbce96f&scene=21#wechat_redirect">脚本之—QQ微信轰炸机</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484909&idx=1&sn=27ab3125be7e9fd31e93fa1cb30bd3ae&chksm=fe7b0ad1c90c83c7158a5679dec8d987a1f05f933efd4073d15ef83a98e897666ed88c235e2f&scene=21#wechat_redirect">脚本之—linux流量监控利器</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484957&idx=1&sn=ad4f9320dfd49a0c7a54042a20bb66c0&chksm=fe7b0921c90c8037a273419352a5677461e9895ca945bd6e33fae2fc401443c6c7a34ef355d9&scene=21#wechat_redirect">脚本之—linux系统巡检脚本合集</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485039&idx=1&sn=ddd14e33325dfd99ac61ee83a4fc198c&chksm=fe7b0953c90c8045e6de3e39cc83b53e067d1a19a051dd17129e8ec2386b36adc9c4d6d1589d&scene=21#wechat_redirect">脚本之—linux批量用户管理脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485046&idx=1&sn=ee3e7a258eafcce88aa65e3daa3fd7be&chksm=fe7b094ac90c805c6ed4e8eb16a698fee1e1b467e948b0e86288aa39cbfebd48ee5e383594c1&scene=21#wechat_redirect">脚本之—根据PID查看相关信息</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485065&idx=1&sn=8913154de8cb68c4679d5a40957eaf18&chksm=fe7b09b5c90c80a323324d176c0be3a3370f1ee3fc9d05625937b7d8c0df206c2e23b6c4773a&scene=21#wechat_redirect">脚本之—一键完成系统安全加固</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485059&idx=1&sn=f4671cd621367c9faf5d697b181e351a&chksm=fe7b09bfc90c80a9d89b23553d1366d17686dd5e3542a17df738d9ead2274fb104943237bc5b&scene=21#wechat_redirect">脚本之—检查系统用户</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485014&idx=1&sn=a633abf6ec6137240528957a16de2f33&chksm=fe7b096ac90c807c09b7881315263c7d6cf87f0fa038d4159b00c2518e5bce619fe2bf68faea&scene=21#wechat_redirect">脚本之—一键同步脚本文件夹</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485111&idx=1&sn=6332d3366f992980cd16862e9974b3c0&chksm=fe7b098bc90c809d65219f1ffe51a03c68c76615bbef5beed46ec59bbea805e1e5d1eb8d8409&scene=21#wechat_redirect">脚本之—一键升/降级k8s集群脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485122&idx=1&sn=d88e6ae5a5327b783c9c7e26fe4615ac&chksm=fe7b09fec90c80e8c61c6e76b6ac514457e9872871a7bb0b6f285297b676307b99bde99278bb&scene=21#wechat_redirect">脚本之—一键升新增k8s集群node节点脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485128&idx=1&sn=3adf091282012695ac2b7d6aadfe00c3&chksm=fe7b09f4c90c80e287710850a2d4912cdea3484537b511c1451a30cd5767d1e7b1af90bbd660&scene=21#wechat_redirect">脚本之—k8s一键部署脚本（多版本通用版）（强推）</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485159&idx=1&sn=a0516b2fb0197e8bd603cbb225d96cf1&chksm=fe7b09dbc90c80cd9fcebdf2285b48d50fd6bdda812565769b413da6b806411e5d634c4ba119&scene=21#wechat_redirect">脚本之—一键部署ceph（luminous）集群脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485197&idx=1&sn=5225f0b73fd496332db24f5cded6c87f&chksm=fe7b0831c90c8127d737569822b00319193fb733a452f65c1e5eb936bc08aa914dcdf584850d&scene=21#wechat_redirect">脚本之—一键部署openvpn脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485203&idx=1&sn=7a4d5359527a6b8be6e7840e2db7fe25&chksm=fe7b082fc90c813968dd6c244bb86b72deb8d68f81b02b7578dfe98db5deb5517ff7d755c747&scene=21#wechat_redirect">脚本之—一键（单双向）互信初始化服务器脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485388&idx=1&sn=d12267364d1066de084b3a02e55da1f0&chksm=fe7b08f0c90c81e6044cea65f30026207dd4bf95098cdb01caf77f371038da8bdcc876b39ce5&scene=21#wechat_redirect">脚本之—一键初始化调优服务器脚本</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247484965&idx=1&sn=50455d6e48c8e58f98a1b2b9ea3c0ae1&chksm=fe7b0919c90c800f7a49d154072edc20a5cee3aca7dbd4e1e292b35664417d0acabfab7e58ff&scene=21#wechat_redirect">脚本之—zabbixAgent一键部署脚本（windos/linux）</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247485248&idx=1&sn=1134c6914570f1b5ad657cb0864d14d1&chksm=fe7b087cc90c816a7b172c552061370bc6db54679af785dbc899580a66d1e4b227c783b66409&scene=21#wechat_redirect">ansible—一键搭建redis5.0.5集群</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzU5NDg5MzM5NQ==&mid=2247483959&idx=1&sn=f1c132a3f3ef3c2cf53166d92bbb1c81&chksm=fe7b0d0bc90c841d865c6ada6ad1b1747636b2ca719906ce5faf36a4775b595fb83cdcbd7988&scene=21#wechat_redirect">工具之—地表最强CMDB自动抓取工具</a></p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>版权声明： </strong>
          
          本博客所有文章除特别声明外，著作权归作者所有。转载请注明出处！
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2020/11/11/linux/Linux%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%AB%98%E5%B9%B6%E5%8F%91%E8%B0%83%E4%BC%98%E5%AE%9E%E6%88%98/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2020/11/11/k8s/%E5%A6%82%E4%BD%95%E7%94%A8%20K8S%20%E6%90%9E%E5%AE%9A%201000%20%E4%B8%AA%E5%BA%94%E7%94%A8%E7%9A%84%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            如何用 K8S 搞定 1000 个应用的测试环境.md
          
        </div>
      </a>
    
    
      <a href="/2020/11/11/linux/Shell%20%E8%84%9A%E6%9C%AC%E7%BC%96%E7%A8%8B%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Shell 脚本编程最佳实践.md</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "",
    app_key: "",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "给我的文章加点评论吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
     
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2020
        <i class="ri-heart-fill heart_icon"></i> TzWind
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="Hexo"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" target="_blank" rel="noopener" href="http://www.baidu.com">百度</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>

</html>